﻿using System.Configuration;
using NUnit.Framework;
using SeleniumToolkit.Util;
using System;
using SeleniumToolkit.Core;

namespace SeleniumToolkit.Config
{
    
    public class SeleniumConfigurationSpecs : SpecFor<SeleniumConfiguration>
    {
        public override SeleniumConfiguration InitializeSubject()
        {
            return new SeleniumConfiguration();
        }

        [TestFixture]
        public class WhenCreatingFromScatch : SeleniumConfigurationSpecs
        {

            [Test]
            public void ShouldHaveDefaultValueForSeleniumServer()
            {
                subject.SeleniumServer.ShouldEqual("localhost");
            }

            [Test]
            public void ShouldHaveDefaultValueForSeleniumPort()
            {
                subject.SeleniumServerPort.ShouldEqual(4444);
            }

            [Test]
            public void ShouldHaveDefaultValueForBrowserString()
            {
                subject.DefaultBrowser.ShouldEqual("*iexplore");
            }

            [Test]
            public void ShouldHaveDefaultValueForBrowserUrl()
            {
                subject.BrowserUrl.ShouldNotBeNull();
            }

            [Test]
            public void ShouldHaveAddinSettingsInitialized()
            {
                subject.AddinSettings.ShouldNotBeNull();
            }

            [Test]
            public void ShouldHaveRecylePerFixtureSettingAsTrue()
            {
                subject.AddinSettings.RecylePerFixture.ShouldBeTrue();
            }

            [Test]
            public void ShouldHaveRuntimeSettingsInitialized()
            {
                subject.RuntimeSettings.ShouldNotBeNull();
            }

            [Test]
            public void ShouldHaveAutoStartSettingAsTrue()
            {
                subject.RuntimeSettings.AutoStart.ShouldBeTrue();
            }

            [Test]
            public void ShouldHaveAutoStopSettingAsTrue()
            {
                subject.RuntimeSettings.AutoStop.ShouldBeTrue();
            }

            [Test]
            public void ShouldHaveBrowserSettingsInitialized()
            {
                subject.Browsers.ShouldNotBeNull();
            }

            [Test]
            public void ShouldResolveDefaultSeleniumFactory()
            {
                subject.ResolveFactory().ShouldBeOfType(typeof(DefaultSeleniumFactory));
            }

        }

        [TestFixture]
        public class WhenRegistryKeyPresent : SeleniumConfigurationSpecs
        {
            protected override void Context()
            {
                var helper = new RegistryKeyHelper(@"SOFTWARE\SeleniumToolkit", true);
                helper.SetValue("InstallDir", @"C:\Temp");
            }

            protected override void CleanUpContext_AfterAllSpecs()
            {
                var helper = new RegistryKeyHelper(@"SOFTWARE\SeleniumTookit");
                if (helper.RegistryKeyExists)
                    helper.Delete();
            }

            [Test]
            public void ShouldFindRegistryKeyValueForSeleniumRCPath()
            {
                // If there are tests with WebFixture or WebTest attributes applied and the addin is installed,
                // the addin will activate.  Changing values in the registry will impact the addin and thus
                // you'll get a runtime error if this file path does not exist.
                subject.RuntimeSettings.SeleniumRuntimePath.ShouldEqual(@"C:\Temp\bin\selenium-rc");
            }

            [Test]
            public void ShouldFindUserValueWhenPresent()
            {
                subject.RuntimeSettings.SeleniumRuntimePath = @"C:\User";

                subject.RuntimeSettings.SeleniumRuntimePath.ShouldEqual(@"C:\User");
            }

        }

        [TestFixture]
        public class WhenRegistryKeyMissing : SeleniumConfigurationSpecs
        {
            protected override void Context()
            {
                RegistryKeyHelper helper = new RegistryKeyHelper(@"SOFTWARE\SeleniumToolkit");
                if (helper.RegistryKeyExists)
                    helper.Delete();
            }

            protected override void Because()
            {
                exception = ((MethodThatThrows) delegate()
                                { 
                                    subject.RuntimeSettings.SeleniumRuntimePath.GetType(); 
                                }
                            ).GetException();
            }

            [Test]
            public void ShouldThrowConfigurationException()
            {
                exception.ShouldNotBeNull();
            }

            [Test]
            public void ShouldWarnAboutMissingConfigurationAttribute()
            {
                exception.ShouldContainErrorMessage("Missing \"location\" attribute from Selenium\\runtime element");
            }
        }

        [TestFixture]
        public class WhenReadingValidConfig : SeleniumConfigurationSpecs
        {
            public override SeleniumConfiguration InitializeSubject()
            {
                Configuration configuration = ConfigHelper.LoadConfig("ValidConfig.config");
                return configuration.GetSection("Selenium") as SeleniumConfiguration;
            }

            [Test]
            public void ShouldBeProperlyResolved()
            {
                subject.ShouldNotBeNull();
            }

            [Test]
            public void ShouldHaveProperServerValue()
            {
                subject.SeleniumServer.ShouldEqual("10.0.0.1");
            }

            [Test]
            public void ShouldHaveProperServerPort()
            {
                subject.SeleniumServerPort.ShouldEqual(3333);
            }

            [Test]
            public void ShouldHaveProperBrowserString()
            {
                subject.DefaultBrowser.ShouldEqual("*firefox");
            }

            [Test]
            public void ShouldHaveProperBrowserUrl()
            {
                subject.BrowserUrl.ShouldEqual("http://www.google.com");
            }

            [Test]
            public void ShouldHaveProperRecylePerFixtureSetting()
            {
                subject.AddinSettings.RecylePerFixture.ShouldBeFalse();
            }

            [Test]
            public void ShouldHaveProperAutoStartRuntimeSetting()
            {
                subject.RuntimeSettings.AutoStart.ShouldBeTrue();
            }

            [Test]
            public void ShouldHaveProperAutoStopRuntimeSetting()
            {
                subject.RuntimeSettings.AutoStop.ShouldBeTrue();
            }

            [Test]
            public void ShouldHaveProperSeleniumRuntimePathSetting()
            {
                subject.RuntimeSettings.SeleniumRuntimePath.ShouldNotBeNull();
            }

            [Test]
            public void ShouldResolveSeleniumFactory()
            {
                subject.ResolveFactory().ShouldNotBeNull();
            }
        }

        [TestFixture]
        public class WhenReadingInvalidConfig : SeleniumConfigurationSpecs
        {
            public override SeleniumConfiguration InitializeSubject()
            {
                return null;
            }

            protected override void Because()
            {
                exception = ((MethodThatThrows) delegate()
                                {
                                    subject = SeleniumConfiguration.GetConfig();
                                }
                            ).GetException();
            }

            [Test]
            public void ShouldNotCreateDefaultConfig()
            {
                subject.ShouldBeNull();
            }

            [Test]
            public void ShouldThrowConfigurationErrorsException()
            {
                exception.ShouldBeOfType(typeof(MissingSeleniumConfigurationException));
            }

            [Test]
            public void ShouldHaveErrorMessageAboutMissingElement()
            {
                exception.ShouldContainErrorMessage("Missing \"Selenium\" configuration element.");
            }
        }

        [TestFixture]
        public class WhenSeleniumFactorySettingProvided : SeleniumConfigurationSpecs
        {
            ISeleniumFactoryProvider provider;

            protected override void Because()
            {
                subject.FactoryType = "SeleniumToolkit.Config.CustomSeleniumFactory, SeleniumToolkit.Tests";
                provider = subject.ResolveFactory();
            }

            [Test]
            public void ShouldCreateCustomProvider()
            {
                provider.ShouldBeOfType(typeof(CustomSeleniumFactory));
            }          
        }

        [TestFixture]
        public class WhenSeleniumFactorySettingIsInvalid : SeleniumConfigurationSpecs
        {
            protected override void Because()
            {
                subject.FactoryType = "this.is.obviously.not.a.valid.type, SeleniumToolkit.Tests";
                exception = ((MethodThatThrows)(() =>
                {
                    subject.ResolveFactory();
                })).GetException();
            }

            [Test]
            public void ShouldThrowTypeLoadException()
            {
                exception.ShouldBeOfType(typeof(TypeLoadException));
            }
        }

        [TestFixture]
        public class WhenSeleniumFactorySettingNotSeleniumFactoryProvider : SeleniumConfigurationSpecs
        {
            protected override void Because()
            {
                subject.FactoryType = "SeleniumToolkit.Config.NotSeleniumFactory, SeleniumToolkit.Tests";
                exception = ((MethodThatThrows)(() => 
                {
                    subject.ResolveFactory();
                })).GetException();
            }

            [Test]
            public void ShouldThrowConfigurationErrorsException()
            {
                exception.ShouldBeOfType(typeof(ConfigurationErrorsException));
            }
        }
    }

    public class CustomSeleniumFactory : ISeleniumFactoryProvider
    {
        #region ISeleniumFactoryProvider Members

        public Selenium.ISelenium Create(string serverHost, int serverPort, string browserProfile, string baseUrl)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    public class NotSeleniumFactory
    {
    }

    internal class ConfigHelper
    {
        public static Configuration LoadConfig(string resourceName)
        {
            var fileMap = new ExeConfigurationFileMap
                              {
                                  ExeConfigFilename = resourceName
                              };
            Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

            return config;
        }
    }
}
